Data Story Group Project#
Jannes de Waard 14363003
Olek Lobman 14638177
Mickey Kotterer 13464167
Olav Mestrum 14504863
Introduction#
Ons project richt zich op het verkennen van de invloed van onderwijs op inkomensniveaus. We willen twee verschillende perspectieven onderzoeken en analyseren om een breder inzicht te krijgen in dit complexe onderwerp. Het eerste perspectief stelt dat een hoog opleidingsniveau wel degelijk invloed heeft op het bereiken van een hoger inkomen. Dit perspectief benadrukt het belang van onderwijs als een cruciale factor bij het bevorderen van professioneel succes en economische groei. Aan de andere kant, presenteren we ook een contradictie: het tweede perspectief stelt dat onderwijs niet noodzakelijk leidt tot hogere inkomensniveaus. Dit perspectief benadrukt andere factoren die van invloed kunnen zijn op iemands inkomen, zoals demografische gegevens, geluk, sociaal kapitaal of economische omstandigheden. Door middel van het analyseren van twee verschillende datasets, zullen we proberen inzicht te krijgen in deze perspectieven en de argumenten die ze ondersteunen. We zullen statistische analyses uitvoeren en trends in de gegevens identificeren om onze bevindingen te ondersteunen. Het doel van dit project is om een grondige evaluatie van beide perspectieven te bieden, en zo een bredere discussie aan te moedigen over de complexe relatie tussen onderwijs en inkomensniveaus. Met dit project hopen we een beter begrip te krijgen van de rol van onderwijs bij het beïnvloeden van inkomens, en bij te dragen aan het bredere debat over de waarde van onderwijs in onze samenleving.
Dataset and preprocessing
#
Dataset 1: US Household Income Statistics
#
Link: https://www.kaggle.com/datasets/goldenoakresearch/us-household-income-stats-geo-locations
Beschrijving: Deze dataset geeft inzicht in de inkomens per huishouden in de Verenigde Staten.
Naast inzicht in het gemiddelde en mediane inkomen, geeft deze dataset ook informatie over
het aantal huishoudens en de bevolkingsdichtheid op verschillende geografische niveaus, zoals
staten, provincies en plaatsen. Deze informatie is nuttig om een analyse te maken van de
inkomensongelijkheid in verschillende gebieden in de Verenigde Staten.
Ten eerste hebben wij deze dataset gevonden op de Kaggle website. Vervolgens hebben we gekeken uit welke kolommen deze dataset bestaat. Hieruit volgde dat alleen de “State” en “mean income” kolom nuttig zijn voor ons onderzoek.
Dataset 2: College tuition, diversity, and pay
#
Link: https://www.kaggle.com/datasets/jessemostipak/college-tuition-diversity-and-pay
Beschrijving: Deze dataset bevat informatie over het collegegeld en de kosten per college/universiteit in de Verenigde Staten voor het academische jaar 2018-2019. Hieronder vallen het type instelling, de duur van de opleiding, de staat en of het in-state of out-of-state is. Met behulp van verschillende tabellen in de dataset kunnen verbanden en inzichten worden verkregen over de kosten, diversiteit, trends en potentieel salaris van hogescholen en universiteiten in de Verenigde Staten.
Deze dataset bestaat uit vijf csv files namelijk: tuition_income, tuition_cost, salary_potential, diversity_school en historical_tuition. De laatste is voor onze datastory niet van belang, daarom hebben we de overige sets gemerged om de belangrijke data in één dataframe te hebben.
Het prepareren van de datasets#
import pandas as pd
import plotly.graph_objs as go
import plotly.express as px
Inkomens dataset#
income_df = pd.read_csv('kaggle_income.csv', encoding='ISO-8859-1')
In de cel hierboven hebben wij een encoding toegevoegd omdat we niet de goede encoding hadden als default. Wij zijn hier achter gekomen met behulp van chatGPT. Het probleem wat zich voordeet luidt als volgt:
209 else:
210 kwargs[new_arg_name] = new_arg_value
--> 211 return func(*args, **kwargs)
File c:\Users\Olav\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\util\_decorators.py:331, in deprecate_nonkeyword_arguments..decorate..wrapper(*args, **kwargs)
325 if len(args) > num_allow_args:
326 warnings.warn(
327 msg.format(arguments=_format_argument_list(allow_args)),
328 FutureWarning,
329 stacklevel=find_stack_level(),
330 )
--> 331 return func(*args, **kwargs)
File c:\Users\Olav\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\io\parsers\readers.py:950, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, error_bad_lines, warn_bad_lines, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)
935 kwds_defaults = _refine_defaults_read(
936 dialect,
937 delimiter,
...
File c:\Users\Olav\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\_libs\parsers.pyx:852, in pandas._libs.parsers.TextReader._tokenize_rows()
File c:\Users\Olav\AppData\Local\Programs\Python\Python311\Lib\site-packages\pandas\_libs\parsers.pyx:1965, in pandas._libs.parsers.raise_parser_error()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf1 in position 20530: invalid continuation byte
Hieruit bleek dat er een aantal de encoding die gebruikt werd bij het inlezen van kaggle_income.csv verkeerd was. Om erachter te komen welke encoding goed is hebben wij een formule gekregen die dat kan detecteren met behulp van chardet. hieruit kwam ISO-8859-1 uit als encoding. Omdat dit notebook gerund moet kunnen worden zonder problemen hebben wij dit opgeslagen en de formule weggehaald. De formule die we gebruikt hebben is:
with open('kaggle_income.csv', 'rb') as f:
raw_bytes = f.read()
detected_encoding = chardet.detect(raw_bytes)['encoding']
Schoolgeld dataset#
tuition_income_df = pd.read_csv('tuition_income.csv')
tuition_cost_df = pd.read_csv('tuition_cost.csv')
salary_potential_df = pd.read_csv('salary_potential.csv')
historical_tuition_df = pd.read_csv('historical_tuition.csv')
diversity_df = pd.read_csv('diversity_school.csv')
Deze dataset kwam als bundel met vijf csv bestanden. Hiervan konden wij er 4 gebruiken en mergen met elkaar. Dit werd een heel grote dataset en er is vervolgens een keuze gemaakt uit de kolommen die wij wilden meenemen in deze story.
tidf = tuition_income_df[['name', 'state', 'total_price', 'year', 'net_cost','income_lvl']]
tcdf = tuition_cost_df[['name', 'state', 'state_code', 'room_and_board', 'in_state_tuition', 'out_of_state_tuition']]
spdf = salary_potential_df[['name', 'state_name', 'early_career_pay', 'mid_career_pay']]
ddf = diversity_df[['name', 'total_enrollment', 'state', 'category',]]
tuition_df = pd.merge(tidf, tcdf, on='name')
tuition_df = pd.merge(tuition_df, spdf, on='name')
tuition_df = pd.merge(tuition_df, ddf, on='name')
Visualisatie 1#
In deze visualisatie wordt er gekeken naar de verdeling van het inkomen in de verenigde staten.
bins = [0, 35000, 50000, 70000, 100000, 150000, 100000000]
income_df['house_income_qcut'] = pd.cut(income_df['Mean'], bins=bins, labels=['<35K', '35K-50K', "50K-70K", '70K-100K', '100K-150K', '>150K'])
category_counts = income_df['house_income_qcut'].value_counts()
datapie = go.Pie(
labels=category_counts.index,
values=category_counts.values,
hole=0.85,
marker = dict(colors=px.colors.qualitative.T10),
textinfo = 'label+percent')
layout = go.Layout(
title='Percentage huishoudens dat binnen inkomensgroep valt',
height=600,)
fig = go.Figure(data=datapie, layout=layout)
fig.show()
figuur 1
In de bovenstaande taartdiagram is te zien hoeveel procent van de huishoudens tot een bepaalde inkomensgroep behoren. De meerderheid zit tussen 50.000 en 100.000
bins = [0, 35000, 50000, 70000, 100000, 150000, 100000000]
income_df['house_income_qcut'] = pd.cut(income_df['Mean'], bins=bins, labels=['<35K', '35K-50K', "50K-70K", '70K-100K', '100K-150K', '>150K'])
data = go.Pie(
labels = income_df['house_income_qcut'],
values = (income_df['Mean']),
hole = 0.85,
marker = dict(colors=px.colors.qualitative.T10),
textinfo = 'label+percent')
layout = go.Layout(
title = 'Cumulatief van geld bij inkomensgroepen huishouden',
height = 600)
fig = go.Figure(data=data, layout=layout)
fig.add_trace(datapie)
fig.show()
figuur 2
Beetje onduidelijk welk punt dit ondersteunt.
In de taartdiagram hierboven is te zien hoeveel de inkomsgroepen cumulatief aan inkomen krijgen van het totaal. De lage inkomensgroepen hebben veel minder procent van het geld in bezit dan zij bij een gelijke verdeling zouden hebben. Om dit te illustreren is er een DataFrame gemaakt die de proporties en verschillen laat zien.
cumul_money = pd.Series([4.03, 13.7, 27.7, 29.3, 21, 4.33])
cumul_people = pd.Series([9.69, 21.3, 31.5, 23.8, 11.9, 1.75])
index = ['<35K', '35K-50K', "50K-70K", '70K-100K', '100K-150K', '>150K']
pie_df = pd.DataFrame({'income_scale' : index, 'percentage_of_money' : cumul_money, 'percentage_of_people' : cumul_people})
pie_df['difference'] = (pie_df['percentage_of_money'] - pie_df['percentage_of_people'])
pie_df['proportion'] = round((pie_df['percentage_of_money'] / pie_df['percentage_of_people']), 2)
display(pie_df)
| income_scale | percentage_of_money | percentage_of_people | difference | proportion | |
|---|---|---|---|---|---|
| 0 | <35K | 4.03 | 9.69 | -5.66 | 0.42 |
| 1 | 35K-50K | 13.70 | 21.30 | -7.60 | 0.64 |
| 2 | 50K-70K | 27.70 | 31.50 | -3.80 | 0.88 |
| 3 | 70K-100K | 29.30 | 23.80 | 5.50 | 1.23 |
| 4 | 100K-150K | 21.00 | 11.90 | 9.10 | 1.76 |
| 5 | >150K | 4.33 | 1.75 | 2.58 | 2.47 |
Uit deze tabel is op te maken dat de lagere inkomensgroepen een kleinere proportie hebben van de totale inkomens. Hieruit is op te maken dat lagere inkomensgroepen weinig geld te besteden hebben op bijvoorbeeld onderwijs. Doordat de prijs van onderwijs kan verschillen zijn zij beperkt in het kiezen van een universiteit
** Tabel visualiseren
Visualisatie 2:#
Dit is een visualisatie van dataset 1, deze bargraph geeft informatie over het gemiddelde inkomen per huishouden per jaar voor iedere staat in de Verenigde Staten. Hierdoor is er duidelijk af te lezen dat de gemiddelden nogal uit elkaar liggen. Met name het verschil tussen District of Columbia en Puerto Rico is aanzienlijk. Puerto Rico is een eiland onder controle van de VS wat niet erg betrokken is bij de economie. Het argument dat gegeven kan worden aan de hand van deze visualisatie, is dat de staat waarin je werkt van invloed is op het inkomen.
tril_df = income_df[['State_Name', 'Mean']]
mid_df = tril_df.groupby('State_Name')[['Mean']].mean().reset_index()
mid_df = mid_df.sort_values('Mean', ascending=False)
mean_income = go.Bar(
x = mid_df['State_Name'],
y = mid_df['Mean'],
name = 'Average househould income'
)
fig2 = go.Figure(data=mean_income)
fig2.show()
figuur 3
** Sorteren op inkomen
Visualisatie 3#
Verschil in collegegeld universiteiten#
Deze visualisatie is een scatterplot van dataset 2. In dit scatterplot staat elk puntje voor een universiteit in een bepaalde staat in de Verenigde Staten. Op de x-as is te lezen hoeveel collegegeld studenten aan deze universiteit betalen als ze zelf uit de staat komen waar de universiteit is gevestigd. Op de y-as is te lezen hoeveel collegegeld studenten betalen als ze studeren aan een universiteit die zich niet bevindt in de staat waarin ze wonen. Deze dataset bevat uitsluitend informatie over Universiteiten waarbij buitenstaatste studenten meer moeten betalen dan binnenstaatse studenten. Dit is zo bij 224 van de 629 universiteiten. Dit is omgerekend 35.6% van de Universiteiten.
same_filt = (tuition_df['in_state_tuition'] != tuition_df['out_of_state_tuition'])
different_tuition_df = tuition_df[same_filt]
fig = px.scatter(different_tuition_df, x=different_tuition_df['in_state_tuition'], y=different_tuition_df['out_of_state_tuition'],
color=different_tuition_df['state_name'],
hover_name=different_tuition_df['name'], log_x=True, size_max=60
)
fig.update_layout(
xaxis=dict(title='Tuition for in state students'),
yaxis=dict(title='Tuition out of state students'),
title='Scatter Plot of Universities only with different tuition fees for out of state studenten'
)
fig.show()
figuur 4
** Deze misschien samenvoegen of iig minder aandacht.
Doordat 35.6% van de universiteiten verschillende tuition fees heeft zijn zij bij elkaar verantwoordelijk voor de verschuiving in het scatterplot hieronder. Deze laat een gestippelde lijn zien bij een gelijke verdeling van tuition fee’s. We hebben de gemiddelden van tuition fees for ‘in state’ en ‘out of state’ studenten gepakt per staat. Daaruit is de onderstaande plot ontstaan.
lil_df = tuition_df[['state_y', 'in_state_tuition', 'out_of_state_tuition']]
mean_df = lil_df.groupby('state_y')[['in_state_tuition', 'out_of_state_tuition']].mean().reset_index()
fig2 = px.scatter(mean_df, x=mean_df['in_state_tuition'], y=mean_df['out_of_state_tuition'],
color=mean_df['state_y'],
hover_name=mean_df['state_y'],
)
fig2.add_trace(go.Scatter(x=[5000, 55000],
y=[5000, 55000],
mode='lines',
name='Equal tuition fees',
line=dict(color='black', dash='dash')))
fig2.update_layout(xaxis_title='In state tuition fee', yaxis_title='Out of state tuition fee')
figuur 5
De derde visualisatie is uit de tweede dataset, deze staafdiagram brengt de tuition fee voor in- en out of state studenten per staat in kaart. Daarnaast is het gemiddelde inkomen per huishouden per staat af te lezen. Wat opvalt is dat staten waar zowel in- als out of state studenten veel tuition fee betalen vaak ook een hoog gemiddeld inkomen hebben. Toch zijn er staten, bijvoorbeeld Indiana, waar het tuition fee voor in state studenten hoog is zonder een hoog gemiddeld inkomen. Dit betekent dat het lastig kan zijn om de kosten voor onderwijs te betalen en veel te verdienen als je bent geboren in een staat waar het gemiddeld inkomen laag is en de tuition fee hoog.
lil_df = tuition_df[['state_y', 'in_state_tuition', 'out_of_state_tuition']]
mean_df = lil_df.groupby('state_y')[['in_state_tuition', 'out_of_state_tuition']].mean().reset_index()
mean_df = mean_df.rename(columns={'state_y' : 'State_Name'})
tril_df = income_df[['State_Name', 'Mean']]
mid_df = tril_df.groupby('State_Name')[['Mean']].mean().reset_index()
filt = (mid_df['State_Name'] != 'District of Columbia') & (mid_df['State_Name'] != 'Puerto Rico')
mid_df = mid_df[filt]
merged_df = pd.merge(mean_df, mid_df, on='State_Name')
merged_df['difference_percentage'] = (abs(merged_df['out_of_state_tuition'] - merged_df['in_state_tuition']) / merged_df['in_state_tuition']) * 100
code = {'Alabama': 'AL','Alaska': 'AK','Arizona': 'AZ',
'Arkansas': 'AR','California': 'CA','Colorado': 'CO','Connecticut': 'CT','Delaware': 'DE',
'District of Columbia': 'DC','Florida': 'FL','Georgia': 'GA','Hawaii': 'HI','Idaho': 'ID',
'Illinois': 'IL','Indiana': 'IN','Iowa': 'IA','Kansas': 'KS','Kentucky': 'KY','Louisiana': 'LA',
'Maine': 'ME','Maryland': 'MD','Massachusetts': 'MA','Michigan': 'MI','Minnesota': 'MN',
'Mississippi': 'MS','Missouri': 'MO','Montana': 'MT','Nebraska': 'NE','Nevada': 'NV',
'New Hampshire': 'NH','New Jersey': 'NJ','New Mexico': 'NM','New York': 'NY',
'North Carolina': 'NC','North Dakota': 'ND','Ohio': 'OH','Oklahoma': 'OK',
'Oregon': 'OR','Pennsylvania': 'PA','Rhode Island': 'RI','South Carolina': 'SC',
'South Dakota': 'SD','Tennessee': 'TN','Texas': 'TX','Utah': 'UT','Vermont': 'VT','Virginia': 'VA',
'Washington': 'WA','West Virginia': 'WV','Wisconsin': 'WI', 'Wyoming': 'WY'}
merged_df['State_Code'] = merged_df['State_Name'].map(code)
fig = px.choropleth(
merged_df,
locations='State_Code',
color='Mean',
color_continuous_scale=[[0, 'white'], [1, 'darkgreen']],
hover_name='State_Name',
locationmode='USA-states',
scope='usa',
labels={'Mean': 'Mean Household Income'}
)
fig.update_traces(
hovertemplate='<b>%{hovertext}</b><br>' +
'Mean: $%{z:.2f}<br>' +
'In-State Tuition: $%{customdata[0]:,.2f}<br>' +
'Out-of-State Tuition: $%{customdata[1]:,.2f}<br>' +
'Difference percentage : %{customdata[2]:,.2f}%',
customdata=merged_df[['in_state_tuition', 'out_of_state_tuition', 'difference_percentage']].values.tolist()
)
fig.update_layout(
title={
'text': 'Mean Household Income per State',
'xanchor': 'center',
'yanchor': 'top',
'x': 0.5
},
coloraxis_colorbar=dict(title='Mean Household Income'),
)
fig.show()
figuur 6
Visualisatie 4#
In de onderstaande scatterdiagram van de tweede dataset valt de correlatie te zien tussen Career pay en Tuition fee voor in-state tuition en out-of-state tuition. In de grafiek is af te lezen dat studenten die meer dan $50 000 tuition betalen, gemiddeld meer verdienen dan studenten die minder tuition betalen. Hieruit kan dus worden geconcludeerd dat het betalen van hoge kosten voor tuition een indicator is voor een hoog inkomen. Echter betekent dit niet dat het betalen van weinig tuition hoe dan ook resulteert in een laag inkomen. Dit is ook af te lezen in de grafiek, er zijn namelijk meerdere gevallen van lage kosten voor tuition maar hoge inkomens.
sub_df = tuition_df[['name','in_state_tuition', 'out_of_state_tuition', 'mid_career_pay']]
fig = px.scatter(
sub_df,
x = sub_df['mid_career_pay'],
y = [sub_df['in_state_tuition'], sub_df['out_of_state_tuition']]
#labels={'mid_career_pay' : 'Estimated career pay', 'in_state_tuition' : 'In state tuition', 'out_of_state_tuition' : 'Out of state tuition'}
)
fig.update_traces(
hovertemplate='<b>%{customdata[0]}</b><br>' +
'In State tuition Fee: $%{customdata[3]}<br>' +
'Out-of-State Tuition Fee: $%{customdata[2]}<br>' +
'Estimated career pay: $%{customdata[1]}',
customdata=sub_df[['name','mid_career_pay', 'out_of_state_tuition', 'in_state_tuition']].values.tolist()
)
fig.show()
figuur 7